#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using UTable.Input;
using UTable.Input.MultiTouch;
using UTable.ObjectManager.Commands;
using UTable.Objects.Controls;
using UTable.Objects.Handlers;
using UTable.Objects.Policies;

namespace UTable.Objects
{
    public class UObjectEntity : Border, IObjectEntity
    {
        #region Private Fields
        private static AutoResetEvent s_event = new AutoResetEvent(false);

        private RotateTransform _rotateTransform;
        private ScaleTransform _scaleTransform;
        private TranslateTransform _translateTransform;
        private NameScope _nameScope;
        private Geometry _clip;
        private UObject _content;
        private String _id;
        private ObjectOperatingState _operatingState;
        private ObjectLayoutState _layoutState;
        private delegate void ContentCommandHandler(ContentCommand cmd);
        private Grid _objectHost;
        private UContentHost _contentHost;
        private bool _contentCreated = false;
        private String _contentID;
        private ControlInfoTree _controlTree;
        private Type _contentType;
        private ObjectCreateParameter _createParameter;
        private bool _informClient = true;

        private Dictionary<AnimationClock, ObjectAnimationInfo> storyboardMap =
            new Dictionary<AnimationClock, ObjectAnimationInfo>();
        private int StoryboardCount = 0;
        private bool _isHitTestEnabled = true;
        private bool _canHoldObjects;
        private bool _notifyLayoutChangeEnabled;
        private IObjectEntity _owner;
        private ObservableCollection<IObjectEntity> _children;
        private List<IObjectEntity> _sortedChildren;
        private ILocalObjectManager _localObjectManager;
        private ObjectComparer _objectComparer;
        private Dictionary<string, IObjectEntity> _IDMap;
        private int _maxZIndex = 0;
        private bool _isActive = false;
        private MultiTouchProcessor _multiTouchProcessor;
        private PropertyType _settgingProperty= PropertyType.None;

        private class ObjectComparer : IComparer<IObjectEntity>
        {
            #region IComparer<IObjectEntity> Members

            public int Compare(IObjectEntity x, IObjectEntity y)
            {
                return y.ZIndex - x.ZIndex;
            }

            #endregion
        }
        private class ObjectAnimationInfo
        {
            public ObjectAnimationArgs Args;
            public String StoryboardName;
            public ObjectAnimationCompletedHandler CompletedHandler;
        }

        #endregion

        #region Constructors

        public UObjectEntity(ObjectCreateParameter parameter)
        {
            if (!parameter.ObjectType.IsSubclassOf(typeof(UObject)))
                throw new Exception("contentType must derive from UObject!");
            _contentType = parameter.ObjectType;
            _createParameter = parameter;
            
            TransformGroup transforms = new TransformGroup();
            this._rotateTransform = new RotateTransform();
            this._scaleTransform = new ScaleTransform();
            this._translateTransform = new TranslateTransform();
            this._translateTransform.X = 0;
            this._translateTransform.Y = 0;
            transforms.Children.Add(this._rotateTransform);
            transforms.Children.Add(this._scaleTransform);
            transforms.Children.Add(this._translateTransform);
            _nameScope = new NameScope();
            NameScope.SetNameScope(this, _nameScope);
            this.RegisterName("TranslateTransform", _translateTransform);
            this.RegisterName("RotateTransform", _rotateTransform);
            this.RegisterName("ScaleTransform", _scaleTransform);
            this.RenderTransform = transforms;

            _objectHost = new Grid();
            Stage = new Canvas();
            _contentHost = new UContentHost();
            _objectHost.Children.Add(_contentHost);
            _objectHost.Children.Add(Stage);
            this.Child = _objectHost;

            // set the multitouch processor;
            _multiTouchProcessor = new MultiTouchProcessor();
            DetectContactsHandler handler = new DetectContactsHandler();
            handler.NewContact += new ContactEventHandler(OnNewContact);
            handler.NoContact += new ContactEventHandler(OnNoContact);
            _multiTouchProcessor.Handlers.Add(handler);

            // _id = UObjectTreeHost.GetInstance().GreateNewID();
            // this.OperatingState = ObjectOperatingState.Automatic;
            
            this.Loaded += new RoutedEventHandler(UObject_Loaded);
            this.Unloaded += new RoutedEventHandler(UObjectEntity_Unloaded);

            _children = new ObservableCollection<IObjectEntity>();
            _children.CollectionChanged += new NotifyCollectionChangedEventHandler(OnChildrenCollectionChanged);
            _sortedChildren = new List<IObjectEntity>();
            _localObjectManager = new ULocalObjectManager();
            _objectComparer = new ObjectComparer();
            _IDMap = new Dictionary<string, IObjectEntity>();

            // create content thread
            if (!_contentCreated)
            {
                if (UTableHelper.Config.MultiThread)
                {
                    CreateSubThread();
                }
                else
                {
                    CreateContentInCurrentThread();
                }
                _contentCreated = true;
            }
        }

        #endregion

        #region Private Methods

        private void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    OnChildAdded(e.NewItems[0] as UObjectEntity);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    OnChildRemoved(e.OldItems[0] as UObjectEntity);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    OnChildAdded(e.NewItems[0] as UObjectEntity);
                    OnChildRemoved(e.OldItems[0] as UObjectEntity);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    foreach (UObjectEntity child in e.OldItems)
                        OnChildRemoved(child);
                    break;
            }
        }

        private void OnChildAdded(UObjectEntity child)
        {
            Canvas.SetTop(child, 0);
            Canvas.SetLeft(child, 0);
            child.ZIndex = _maxZIndex;
            _maxZIndex++;
            Stage.Children.Add(child);
            _sortedChildren.Add(child);
            _IDMap.Add(child.ID, child);

            if (ChildrenChanged != null)
            {
                ChildrenChanged(this, new ObjectChildrenChangedEventArgs(child, null));
            }
        }

        private void OnChildRemoved(UObjectEntity child)
        {
            Stage.Children.Remove(child);
            _sortedChildren.Remove(child);
            _IDMap.Remove(child.ID);

            if (ChildrenChanged != null)
            {
                ChildrenChanged(this, new ObjectChildrenChangedEventArgs(null, child));
            }
        }

        private void UObject_Loaded(object sender, RoutedEventArgs e)
        {
            // Create the policies if needed
            if (_content.LayoutPolicyType != null)
            {
                this.LocalObjectManager.LayoutPolicy =
                Activator.CreateInstance(_content.LayoutPolicyType,
                    new object[] { this, _content.LayoutPolicyParameter }) as LayoutPolicy;
            }
        }

        private void UObjectEntity_Unloaded(object sender, RoutedEventArgs e)
        {
            // Console.WriteLine("UObject Entity Unloaded");
        }

        private class SubThreadStartupParameters
        {
            public HostVisual HostVisual { get; set; }

            public ObjectCreateParameter CreateParameter { get; set; }

            public Type ContentType { get; set; }

            public Size Size { get; set; }

            public Point Position { get; set; }

            public double Orientation { get; set; }

            public Size Scale { get; set; }

            public Geometry Clip { get; set; }

            public Brush Background { get; set; }

            public Visibility Visibility { get; set; }

            public bool IsHitTsetEnabled { get; set; }

            public bool IsHitTestVisible { get; set; }

            public double Opacity { get; set; }

            public Point RotateCenter { get; set; }

            public Point ScaleCenter { get; set; }

            public Size MinSize { get; set; }

            public Size MaxSize { get; set; }

            public ObjectOperatingState OperatingState { get; set; }

            public ObjectLayoutState LayoutState { get; set; }

            public int ZIndex { get; set; }

            public bool CanHoldObjects { get; set; }

            public bool NotifyLayoutChangeEnabled { get; set; }
        }

        private void CreateSubThread()
        {
            // Create the HostVisual that will "contain" the VisualTarget
            // on the worker thread.
            HostVisual hostVisual = new HostVisual();
            // Spin up a worker thread, and pass it the HostVisual that it
            // should be part of.
            ContentThread = new Thread(new ParameterizedThreadStart(SubThread));
            ContentThread.SetApartmentState(ApartmentState.STA);
            ContentThread.IsBackground = true;

            SubThreadStartupParameters param = new SubThreadStartupParameters();
            param.HostVisual = hostVisual;
            param.CreateParameter = _createParameter;
            param.ContentType = _contentType;

            ContentThread.Start(param);

            // Wait for the worker thread to spin up and create the VisualTarget.
            s_event.WaitOne();

            _contentHost.Child = hostVisual;
            this.Width = param.Size.Width;
            this.Height = param.Size.Height;
            this.MinHeight = param.MinSize.Height;
            this.MaxHeight = param.MaxSize.Height;
            this.MinWidth = param.MinSize.Width;
            this.MaxWidth = param.MaxSize.Width;
            this.Position = param.Position;
            this.Orientation = param.Orientation;
            this.RotateCenter = param.RotateCenter;
            this.ScaleX = param.Scale.Width;
            this.ScaleY = param.Scale.Height;
            this.ScaleCenter = param.ScaleCenter;
            this.Visibility = param.Visibility;
            this.Opacity = param.Opacity;
            this.IsHitTestEnabled = param.IsHitTsetEnabled;
            this.IsHitTestVisible = param.IsHitTestVisible;
            this.LayoutState = param.LayoutState;
            this.OperatingState = param.OperatingState;
            this.ZIndex = param.ZIndex;
            this.CanHoldObjects = param.CanHoldObjects;
            this.NotifyLayoutChangeEnabled = param.NotifyLayoutChangeEnabled;
            SetProperty(PropertyType.Background, param.Background, false);
            BindContent();
        }

        private void SubThread(object arg)
        {
            try
            {
	            // Create the VisualTargetPresentationSource and then signal the
	            // calling thread, so that it can continue without waiting for us.
	
	            SubThreadStartupParameters param = (SubThreadStartupParameters)arg;
	            HostVisual hostVisual = param.HostVisual;
	            VisualTargetPresentationSource visualTargetPS = new VisualTargetPresentationSource(hostVisual);
	
	            // Create an object content and use it as the root visual for the VisualTarget.
	            this._content = Activator.CreateInstance(param.ContentType) as UObject;
	            this._content.IsDesigning = true;
	            this._content.Entity = this;
	            this._content.Info.Entity = this;
				this._content.PresentationSource = visualTargetPS;
	            _contentID = _content.ID;
	            _controlTree = _content.ControlTree;
				_id = _contentID;
	
	            UTableHelper.RegisterObject(_content);
	            UTableHelper.RegisterVisualTree(_content);
	            UTableHelper.RegisterInputManager(_content.InputManager);
	            
	
	            visualTargetPS.RootVisual = _content;
	
	            // Initialize the size of content and object
	            param.Size = new Size(_content.Width, _content.Height);
	            param.MinSize = new Size(_content.MinWidth, _content.MinHeight);
	            param.MaxSize = new Size(_content.MaxWidth, _content.MaxHeight);
	            param.Position = _content.Position;
	            param.Orientation = _content.Orientation;
	            param.RotateCenter = _content.RotateCenter;
	            param.ScaleCenter = _content.ScaleCenter;
	            param.Scale = new Size(_content.ScaleX, _content.ScaleY);
	            param.Visibility = _content.Visibility;
	            param.IsHitTestVisible = _content.IsHitTestVisible;
	            param.IsHitTsetEnabled = _content.IsHitTestEnabled;
	            param.Opacity = _content.Opacity;
	            param.LayoutState = _content.LayoutState;
	            param.OperatingState = _content.OperatingState;
	            param.ZIndex = _content.ZIndex;
	            param.CanHoldObjects = _content.CanHoldObjects;
	            param.NotifyLayoutChangeEnabled = _content.NotifyLayoutChangeEnabled;
	            if (_content.Clip != null)
	            {
	                Geometry c = _content.Clip.CloneCurrentValue();
	                c.Freeze();
	                contentPropertyChanged(PropertyType.Clip, c);
	            }
	            if (_content.Background != null)
	            {
	                Brush background = _content.Background.CloneCurrentValue();
	                background.Freeze();
	                // contentPropertyChanged(PropertyType.Background, background);
	                param.Background = background;
	            }
	
	            this.OnContentCreated(_content);
	            _content.IsDesigning = false;
	
	            this.Dispatcher.BeginInvoke(new EventHandler(InvokeObjectLoaded), new object[] { null, null });
	
	            s_event.Set();
	
	            // Run a dispatcher for this worker thread.  This is the central
	            // processing loop for WPF.
	            if (UTableHelper.DevelopMode)
	            {
	                try
	                {
	                    System.Windows.Threading.Dispatcher.Run();
	                }
	                catch (System.Exception ex)
	                {
	                    MessageBox.Show(ex.ToString());
	                }
	            }
	            else
	            {
	                try
	                {
	                    System.Windows.Threading.Dispatcher.Run();
	                }
	                catch (System.Exception e)
	                {
	                    // Console.WriteLine(e.StackTrace);
	                    // UTableHelper.PutCommand(new ObjectCommand(this.Owner, ObjectCommandType.Close, this));
	
	                    // TODO
	                    // handle this exception, and inform the user
	                    throw e;
	                }
	            }
	            // TODO
	            // finish closing the object
	            UTableHelper.UnregisterObject(_content);
	            UTableHelper.UnregisterVisualTree();
	            UTableHelper.UnregisterInputManager();
	
	            // Console.WriteLine(this._content + " shut down");
	            this._content = null;
	            _contentHost = null;
	            this.Dispatcher.BeginInvoke(new EventHandler(OnChildClosed), new object[] { null, null });
	            // GC.Collect();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private void CreateContentInCurrentThread()
        {
            // Create an object content and use it as the root visual for the VisualTarget.
            this._content = Activator.CreateInstance(_contentType) as UObject;
            this._content.IsDesigning = true;
            this._content.Entity = this;
            this._content.Info.Entity = this;
            _contentID = _content.ID;
            _controlTree = _content.ControlTree;
            _id = _contentID;

            // UTableHelper.RegisterObject(_content);
            // UTableHelper.RegisterVisualTree(_content);
            // UTableHelper.RegisterInputManager(_content.InputManager);

            // Initialize the size of content and object
            this.Width = _content.Width;
            this.Height = _content.Height;
            this.MinWidth = _content.MinWidth;
            this.MinHeight = _content.MinHeight;
            this.MaxWidth = _content.MaxWidth;
            this.MaxWidth = _content.MaxWidth;
            this.Position = _content.Position;
            this.Orientation = _content.Orientation;
            this.RotateCenter = _content.RotateCenter;
            this.ScaleCenter = _content.ScaleCenter;
            this.ScaleX = _content.ScaleX;
            this.ScaleY = _content.ScaleY;
            this.Visibility = _content.Visibility;
            this.IsHitTestVisible = _content.IsHitTestVisible;
            this.IsHitTestEnabled = _content.IsHitTestEnabled;
            this.Opacity = _content.Opacity;
            this.LayoutState = _content.LayoutState;
            this.OperatingState = _content.OperatingState;
            this.ZIndex = _content.ZIndex;
            this.CanHoldObjects = _content.CanHoldObjects;
            this.NotifyLayoutChangeEnabled = _content.NotifyLayoutChangeEnabled;
            if (_content.Clip != null)
            {
                Geometry c = _content.Clip.CloneCurrentValue();
                c.Freeze();
                contentPropertyChanged(PropertyType.Clip, c);
            }
            if (_content.Background != null)
            {
                Brush background = _content.Background.CloneCurrentValue();
                background.Freeze();
                // contentPropertyChanged(PropertyType.Background, background);
            }

            // _contentHost.Child = _content;
            this.Child = _content;
            this.OnContentCreated(_content);
            _content.IsDesigning = false;
            InvokeObjectLoaded(null, null);
        }

        private void OnChildClosed(object sender, EventArgs args)
        {
            this.Child = null;
            this.Closed -= new EventHandler(UObjectEntity_Closed);
            this.LayoutChanged -= new ObjectLayoutChangedEventHandler(UObjectEntity_LayoutChanged);
            this.ObjectLoaded -= new EventHandler(UObjectEntity_ObjectLoaded);
            this.LayoutStateChanged -= new EventHandler(UObjectEntity_LayoutStateChanged);
            this.OperatingStateChanged -= new EventHandler(UObjectEntity_OperatingStateChanged);
            this.OwnerChanged -= new EventHandler(UObjectEntity_OwnerChanged);
            this.ChildrenChanged -= new ObjectChildrenChangedEventHandler(UObjectEntity_ChildrenChanged);
            this.DragStarted -= new ObjectDragEventHandler(UObjectEntity_DragStarted);
            this.DragEnded -= new ObjectDragEventHandler(UObjectEntity_DragEnded);
            this.Loaded -= new RoutedEventHandler(UObject_Loaded);
            this.Unloaded -= new RoutedEventHandler(UObjectEntity_Unloaded);
            _children.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnChildrenCollectionChanged);

            if (LocalObjectManager.LayoutPolicy != null)
            {
                this.LocalObjectManager.LayoutPolicy.OnClosed();
            }
        }

        private void InvokeObjectLoaded(object sender, EventArgs args)
        {
            if (this.ObjectLoaded != null)
                this.ObjectLoaded(this, null);
        }

        private void ContentCommandReceived(object sender, CommandEventArgs args)
        {
            ObjectCommand command = args.Command as ObjectCommand;
            if (command == null)
                return;
            command.Receiver = this;
            UTableHelper.PutCommand(command);
        }

        private void SendContentCommand(ContentCommand cmd)
        {
            if (cmd.Param is MultiTouchEventArgs)
                (cmd.Param as UTable.Input.MultiTouch.MultiTouchEventArgs).FingerEvents[0].TimeTrace.BeforeSendUObjectTime = InputEventArgs.GetTimestamp();
            // (cmd.Param as UTable.Input.MultiTouch.MultiTouchEventArgs).FingerEvents[0].TimeTrace.SetStep(2);

            if (UTableHelper.Config.MultiThread)
                _content.Dispatcher.BeginInvoke(new ContentCommandHandler(_content.ExecuteContentCommand), new object[] { cmd });
            else
                _content.ExecuteContentCommand(cmd);
        }

        private void contentPropertyChanged(PropertyType type, object value)
        {
            this.Dispatcher.BeginInvoke(new SetPropertyHandler(SetProperty),
                new object[] { type, value });
        }

        private void ChangeContentLayoutProperty(UObject.LayoutPropertyChangedEventArgs args)
        {
            if (_content != null)
                _content.Dispatcher.BeginInvoke(new UObject.LayoutPropertyChangedEventHandler(_content.ChangeLayoutProperty), 
                    new object[] { args });
        }

        void OnNoContact(FingerEventArgs args)
        {
            IsActive = false;
        }

        void OnNewContact(FingerEventArgs args)
        {
            IsActive = true;
        }

        #endregion

        #region Internal Methods

        private void BindContent()
        {
            this.Closed += new EventHandler(UObjectEntity_Closed);
            this.LayoutChanged += new ObjectLayoutChangedEventHandler(UObjectEntity_LayoutChanged);
            this.ObjectLoaded += new EventHandler(UObjectEntity_ObjectLoaded);
            this.LayoutStateChanged += new EventHandler(UObjectEntity_LayoutStateChanged);
            this.OperatingStateChanged += new EventHandler(UObjectEntity_OperatingStateChanged);
            this.OwnerChanged += new EventHandler(UObjectEntity_OwnerChanged);
            this.ChildrenChanged += new ObjectChildrenChangedEventHandler(UObjectEntity_ChildrenChanged);
            this.DragStarted += new ObjectDragEventHandler(UObjectEntity_DragStarted);
            this.DragEnded += new ObjectDragEventHandler(UObjectEntity_DragEnded);
        }

        private void RaiseContentEvent(ObjectEventType type, object[] param)
        {
            _content.Dispatcher.BeginInvoke(new UObject.RaiseObjectEventHandler(_content.RaiseObjectEvent), 
                new object[] { type, param });
        }

        private void UObjectEntity_OperatingStateChanged(object sender, EventArgs e)
        {
            RaiseContentEvent(ObjectEventType.OperatingStateChanged, new object[] { sender, e });
        }

        private void UObjectEntity_LayoutStateChanged(object sender, EventArgs e)
        {
            RaiseContentEvent(ObjectEventType.LayoutStateChanged, new object[] { sender, e });
        }

        private void UObjectEntity_ObjectLoaded(object sender, EventArgs e)
        {
            RaiseContentEvent(ObjectEventType.ObjectLoaded, new object[] { sender, e });
        }

        private void UObjectEntity_LayoutChanged(IObject sender, ObjectLayoutChangedEventArgs args)
        {
            if (NotifyLayoutChangeEnabled)
                RaiseContentEvent(ObjectEventType.LayoutChanged, new object[] { sender, args });
        }

        private void UObjectEntity_Closed(object sender, EventArgs e)
        {
            RaiseContentEvent(ObjectEventType.Closed, new object[] { sender, e });
        }

        void UObjectEntity_ChildrenChanged(IObject sender, ObjectChildrenChangedEventArgs args)
        {
            // recreate the args, so the properties are UObjectProxy instead of UObjectEntity
            IObject removed = null;
            IObject added = null;
            if (args.Added != null && args.Added is UObjectEntity)
                added = new UObjectProxy(args.Added as UObjectEntity);
            if (args.Removed != null && args.Removed is UObjectEntity)
                removed = new UObjectProxy(args.Removed as UObjectEntity);
            args = new ObjectChildrenChangedEventArgs(added, removed);

            RaiseContentEvent(ObjectEventType.ChildrenChanged, new object[] { sender, args });
        }

        void UObjectEntity_OwnerChanged(object sender, EventArgs e)
        {
            RaiseContentEvent(ObjectEventType.OwnerChanged, new object[] { sender, e });
        }

		void UObjectEntity_DragEnded(IObject sender, ObjectDragEventArgs args)
		{
			RaiseContentEvent(ObjectEventType.DragStarted, new object[] { sender, args });
		}

		void UObjectEntity_DragStarted(IObject sender, ObjectDragEventArgs args)
		{
			RaiseContentEvent(ObjectEventType.DragEnded, new object[] { sender, args });
		}

        internal protected virtual void OnContentCreated(UObject content)
        {

        }

        // TODO
        // Convert the request to command and send them to the global object manager
        internal void SetProperty(PropertyType type, object value, bool inforClient)
        {
            if (inforClient)
            {
                _informClient = true;
                _settgingProperty = PropertyType.None;
            }
            else
            {
                _informClient = false;
                _settgingProperty = type;
            }
            switch (type)
            {
                case PropertyType.Position:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.Translate, value));
                    break;
                case PropertyType.Width:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.Resize, new Size((double)value, this.Height)));
                    break;
                case PropertyType.Height:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.Resize, new Size(this.Width, (double)value)));
                    break;
                case PropertyType.Clip:
                    Geometry clip = null;
                    if (value != null)
                        clip = ((Geometry)value).CloneCurrentValue();
                    // this.Clip = clip;
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeClip, clip));
                    break;
                case PropertyType.Background:
                    if (value != null)
                        this.Background = ((Brush)value).CloneCurrentValue();
                    else
                        this.Background = null;
                    break;
                case PropertyType.IsHitTestVisible:
                    this.IsHitTestVisible = (bool)value;
                    break;
                case PropertyType.IsHitTestEnabled:
                    this.IsHitTestEnabled = (bool)value;
                    break;
                case PropertyType.Opacity:
                    this.Opacity = (double)value;
                    break;
                case PropertyType.Visibility:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeVisibility, value));
                    break;
                case PropertyType.Orientation:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.Rotate, value));
                    break;
                case PropertyType.RotateCenter:
                    // this.RotateCenter = (Point)value;
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeRotateCenter, value));
                    break;
                case PropertyType.ScaleX:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.Scale, new Size((double)value, ScaleY)));
                    break;
                case PropertyType.ScaleY:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.Scale, new Size(ScaleX, (double)value)));
                    break;
                case PropertyType.ScaleCenter:
                    // this.ScaleCenter = (Point)value;
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeScaleCenter, new Size(ScaleX, (double)value)));
                    break;
                case PropertyType.ZIndex:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeZIndex, value));
                    break;
                case PropertyType.Owner:
                    this.SetOwner((IObject)value);
                    break;
                case PropertyType.OperatingState:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeOperatingState, value));
                    break;
                case PropertyType.LayoutState:
                    UTableHelper.PutCommand(new ObjectCommand(this, ObjectCommandType.ChangeLayoutState, value));
                    break;
                case PropertyType.CanHoldObjects:
                    this.CanHoldObjects = (bool)value;
                    break;
                case PropertyType.NotifyLayoutChangeEnabled:
                    this.NotifyLayoutChangeEnabled = (bool)value;
                    break;
            }
            _settgingProperty =  PropertyType.None;
            _informClient = true;
        }

        internal object GetProperty(PropertyType type)
        {
            switch (type)
            {
                case PropertyType.Position:
                    return this.Position;
                case PropertyType.Orientation:
                    return this.Orientation;
                case PropertyType.RotateCenter:
                    return this.RotateCenter;
                case PropertyType.MinWidth:
                    return this.MinWidth;
                case PropertyType.MaxWidth:
                    return this.MaxWidth;
                case PropertyType.MinHeight:
                    return this.MinHeight;
                case PropertyType.MaxHeight:
                    return this.MaxHeight;
                case PropertyType.Width:
                    return this.Width;
                case PropertyType.Height:
                    return this.Height;
                case PropertyType.ScaleX:
                    return this.ScaleX;
                case PropertyType.ScaleY:
                    return this.ScaleY;
                case PropertyType.ScaleCenter:
                    return this.ScaleCenter;
                case PropertyType.ZIndex:
                    return this.ZIndex;
                case PropertyType.Owner:
                    if (this.Owner == null || !(this.Owner is UObjectEntity))
                        return null;
                    else
                        return (this.Owner as UObjectEntity).CreateObjectProxy();
                case PropertyType.OwnedObjects:
                    List<IObject> objects = new List<IObject>();
                    foreach (UObjectEntity obj in OwnedObjects)
                    {
                        objects.Add(obj.CreateObjectProxy());
                    }
                    return new ReadOnlyCollection<IObject>(objects);
                case PropertyType.OperatingState:
                    return this.OperatingState;
                case PropertyType.LayoutState:
                    return this.LayoutState;
                case PropertyType.CanHoldObjects:
                    return this.CanHoldObjects;
                case PropertyType.NotifyLayoutChangeEnabled:
                    return this.NotifyLayoutChangeEnabled;
                case PropertyType.IsHitTestVisible:
                    return this.IsHitTestVisible;
                case PropertyType.IsHitTestEnabled:
                    return this.IsHitTestEnabled;
                case PropertyType.ObjectType:
                    return this.ObjectType;
            }
            return null;
        }

        internal object CallMethod(MethodType type, object[] param)
        {
            switch (type)
            {
                case MethodType.BeginAnimation:
                    this.BeginAnimation(param[0] as ObjectAnimationArgs,
                        param[1] as ObjectAnimationCompletedHandler);
                    return null;
                case MethodType.Close:
                    this.Close();
                    return null;
                case MethodType.TranslatePoint:
                    return this.TranslatePoint((Point)param[0], 
                        param[1] as IObject);
                case MethodType.CaptureFinger:
                    return this.CaptureFinger((int)param[0]);
                case MethodType.UncaptureFinger:
                    return this.UncaptureFinger((int)param[0]);
                case MethodType.OnInputReceived:
                    this.OnInputReceived((InputEventArgs)param[0]);
                    return null;
                case MethodType.RaiseEvent:
                    this.RaiseEvent((InputEventArgs)param[0]);
                    return null;
                case MethodType.SendMessage:
                    this.SendMessage(param[0]);
                    return null;
            }
            return null;
        }

        internal IObject CreateObjectProxy()
        {
            return new UObjectProxy(this);
        }

        internal enum MethodType
        {
            TranslatePoint, 
            Close,
            BeginAnimation,
            RaiseEvent,
            OnInputReceived,
            CaptureFinger,
            UncaptureFinger,
            SendMessage,
        }

        internal enum PropertyType
        {
            None,
            ID,
            Width,
            Height,
            Clip,
            Opacity,
            Visibility,
            Background,
            ObjectType,
            IsHitTestVisible,
            IsHitTestEnabled,
            MinWidth,
            MaxWidth,
            MinHeight,
            MaxHeight,
            Position,
            Orientation,
            RotateCenter,
            ScaleX,
            ScaleY,
            ScaleCenter,
            ZIndex,
            Owner,
            OwnedObjects,
            OperatingState,
            LayoutState,
            CanHoldObjects,
            NotifyLayoutChangeEnabled,
        }

        internal delegate void SetPropertyHandler(PropertyType type, object value, bool informClient);

        internal delegate object GetPropertyHandler(PropertyType type);

        internal delegate object CallMethodHandler(MethodType type, object[] param);

        #endregion

        #region Properties

        public MultiTouchProcessor MultiTouchProcessor
        {
            get { return _multiTouchProcessor; }
        }

        public Canvas Stage { get; set; }

        public bool IsActive
        {
            get { return _isActive; }
            set 
            { 
                if (_isActive != null)
                {
                    _isActive = value;
                    RaiseContentEvent(ObjectEventType.IsActiveChanged, new object[] { value });
                }
            }
        }

		public UObject Content { get { return _content; } }

        internal Thread ContentThread { get; set; }

        #endregion

        #region IObject Members

        public new double MinWidth
        {
            get
            {
                return base.MinWidth;
            }
            set
            {
                if (MinWidth != value)
                {
                    base.MinWidth = value;
                    if (_informClient)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.MinWidth, value));
                }
            }
        }

        public new double MinHeight
        {
            get
            {
                return base.MinHeight;
            }
            set
            {
                if (MinWidth != value)
                {
                    base.MinHeight = value;
                    if (_informClient)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.MinHeight, value));
                }
            }
        }

        public new double MaxWidth
        {
            get
            {
                return base.MaxWidth;
            }
            set
            {
                if (MaxWidth != value)
                {
                    base.MaxWidth = value;
                    if (_informClient)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.MaxWidth, value));
                }
            }
        }

        public new double MaxHeight
        {
            get
            {
                return base.MaxHeight;
            }
            set
            {
                if (MaxHeight != value)
                {
                    base.MaxHeight = value;
                    if (_informClient)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.MaxHeight, value));
                }
            }
        }

        public Point Position
        {
            get
            {
                return new Point(_translateTransform.X, _translateTransform.Y);
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (_translateTransform.X != value.X || _translateTransform.Y != value.Y)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Position, Position, value);
                _translateTransform.X = value.X;
                _translateTransform.Y = value.Y;
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public double Orientation
        {
            get
            {
                return _rotateTransform.Angle;
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (Orientation != value)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Orientation, Orientation, value);
                _rotateTransform.Angle = value;
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public Point RotateCenter
        {
            get
            {
                return new Point(_rotateTransform.CenterX, _rotateTransform.CenterY);
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (RotateCenter != value)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.RotateCenter, RotateCenter, value);
                this._rotateTransform.CenterX = value.X;
                this._rotateTransform.CenterY = value.Y;
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public double ScaleX
        {
            get
            {
                return _scaleTransform.ScaleX;
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (ScaleX != value)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Scale, new Point(ScaleX, ScaleY), new Point(value, ScaleX));
                _scaleTransform.ScaleX = value;
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public double ScaleY
        {
            get
            {
                return _scaleTransform.ScaleY;
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (ScaleY != value)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Scale, new Point(ScaleX, ScaleY), new Point(ScaleX, value));
                _scaleTransform.ScaleY = value;
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public Point ScaleCenter
        {
            get
            {
                return new Point(_scaleTransform.CenterX, _scaleTransform.CenterY);
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (ScaleCenter != value)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.ScaleCenter, ScaleCenter, value);
                _scaleTransform.CenterX = value.X;
                _scaleTransform.CenterY = value.Y;
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public int ZIndex
        {
            get
            {
                return Canvas.GetZIndex(this);
            }
            set
            {
                ObjectLayoutChangedEventArgs args = null;
                if (ZIndex != value)
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.ZIndex, ZIndex, value);
                Canvas.SetZIndex(this, value);
                if (args != null && LayoutChanged != null && NotifyLayoutChangeEnabled)
                    LayoutChanged(this, args);
            }
        }

        public ReadOnlyCollection<IObject> OwnedObjects
        {
            get 
            {
                List<IObject> children = new List<IObject>();
                foreach (IObject child in Children)
                    children.Add(child);
                return new ReadOnlyCollection<IObject>(children);
            }
        }

        public IObject Owner
        {
            get 
            {
                return _owner;
            }
            set
            {
                if (Owner != value)
                {
                    SetOwner(value);
                }
            }
        }

        public void SetOwner2(IObject owner)
        {
            _owner = owner as UObjectEntity;
            if (OwnerChanged != null)
                OwnerChanged(this, EventArgs.Empty);
        }

        public void SetOwner(IObject owner)
        {
            UObjectEntity newParent = null;
            if (owner is UObjectEntity)
            {
                newParent = owner as UObjectEntity;
            }
            if (owner is UObject)
            {
                newParent = (owner as UObject).Entity;
            }
            if (owner is UObjectProxy)
            {
                newParent = (owner as UObjectProxy).Entity;
            }
            if (newParent == this.Owner)
                return;
            if (this.Owner != null)
            {
                UTableHelper.PutCommand(new ObjectCommand(this.Owner, ObjectCommandType.Remove, this));
            }
            if (newParent != null)
                UTableHelper.PutCommand(new ObjectCommand(newParent, ObjectCommandType.Insert, this));
        }

        public ObjectOperatingState OperatingState
        {
            get
            {
                return _operatingState;
            }
            set
            {
                if (_operatingState != value)
                {
                    _operatingState = value;
                    if (OperatingStateChanged != null)
                        OperatingStateChanged(this, EventArgs.Empty);
                }
            }
        }

        public ObjectLayoutState LayoutState
        {
            get
            {
                return _layoutState;
            }
            set
            {
                if (_layoutState != value)
                {
                    _layoutState = value;
                    if (LayoutStateChanged != null)
                        LayoutStateChanged(this, EventArgs.Empty);
                }
            }
        }

        public bool CanHoldObjects
        {
            get
            {
                return _canHoldObjects;
            }
            set
            {
                _canHoldObjects = value;
            }
        }

        public bool NotifyLayoutChangeEnabled
        {
            get
            {
                return _notifyLayoutChangeEnabled;
            }
            set
            {
                _notifyLayoutChangeEnabled = value;
            }
        }


        public Type ObjectType
        {
            get { return _contentType; }
        }

        public Point TranslatePoint(Point point, IObject relativeTo)
        {
            if (relativeTo is UObject)
                relativeTo = (relativeTo as UObject).Entity;
            else if (relativeTo is UObjectProxy)
                relativeTo = (relativeTo as UObjectProxy).Entity;
            return this.TranslatePoint(point, relativeTo as IVisualNode);
        }

        public void SendMessage(object message)
        {
            _content.Dispatcher.BeginInvoke(new SendMessageHandler(_content.SendMessage), new object[] { message });
        }

		public void QueryData(object dataFlag, QueryDataCallback callback)
		{
			_content.Dispatcher.BeginInvoke(new QueryDataAsyncHandler(_content.QueryData), new object[] { dataFlag, callback });
		}

		public object QueryData(object dataFlag)
		{
			return _content.Dispatcher.Invoke(new QueryDataSyncHandler(_content.QueryData), new object[] { dataFlag });
		}

        public void BeginAnimation(ObjectAnimationArgs args, ObjectAnimationCompletedHandler completedCallback)
        {
            Storyboard storyboard = new Storyboard();
            AnimationTimeline animation = args.Animation;
            if (animation == null || !(animation is DoubleAnimationBase))
                return;
            animation = animation.CloneCurrentValue() as AnimationTimeline;

            // set animation target
            switch (args.DstProperty)
            {
                case ObjectAnimationDstType.Orientation:
                    Storyboard.SetTargetName(animation, "RotateTransform");
                    Storyboard.SetTargetProperty(animation, new PropertyPath(RotateTransform.AngleProperty));
                    break;
                case ObjectAnimationDstType.PositionX:
                    Storyboard.SetTargetName(animation, "TranslateTransform");
                    Storyboard.SetTargetProperty(animation, new PropertyPath(TranslateTransform.XProperty));
                    break;
                case ObjectAnimationDstType.PositionY:
                    Storyboard.SetTargetName(animation, "TranslateTransform");
                    Storyboard.SetTargetProperty(animation, new PropertyPath(TranslateTransform.YProperty));
                    break;
                case ObjectAnimationDstType.ScaleX:
                    Storyboard.SetTargetName(animation, "ScaleTransform");
                    Storyboard.SetTargetProperty(animation, new PropertyPath(ScaleTransform.ScaleXProperty));
                    break;
                case ObjectAnimationDstType.ScaleY:
                    Storyboard.SetTargetName(animation, "ScaleTransform");
                    Storyboard.SetTargetProperty(animation, new PropertyPath(ScaleTransform.ScaleYProperty));
                    break;
            }
            storyboard.Children.Add(animation);
            AnimationClock clock = animation.CreateClock();
            clock.Completed += new EventHandler(animation_Completed);

            // start animation
            StoryboardCount++;
            String storyboardName = "MoveStroyBoard" + StoryboardCount;
            this.Resources.Add(storyboardName, storyboard);
            ObjectAnimationInfo info = new ObjectAnimationInfo();
            info.Args = args;
            info.CompletedHandler = completedCallback;
            info.StoryboardName = storyboardName;
            
            lock (storyboardMap)
            {
                storyboardMap.Add(clock, info);
            }
            storyboard.Begin();
        }

        void animation_Completed(object sender, EventArgs e)
        {
            ObjectAnimationInfo info = null;
            AnimationClock clock = sender as AnimationClock;
            lock (storyboardMap)
            {
                info = storyboardMap[clock];
                storyboardMap.Remove(clock);
            }
            switch (info.Args.DstProperty)
            {
                case ObjectAnimationDstType.PositionX:
                    this.Position = new Point((info.Args.Animation as DoubleAnimationBase).GetCurrentValue(0, 0, clock), this.Position.Y);
                    break;
                case ObjectAnimationDstType.PositionY:
                    break;
                case ObjectAnimationDstType.ScaleX:
                    break;
                case ObjectAnimationDstType.ScaleY:
                    break;
                case ObjectAnimationDstType.Orientation:
                    break;
            }

            Storyboard storyboard = (this.Resources[info.StoryboardName] as Storyboard);
            storyboard.SkipToFill();
            // storyboard.Stop();
            this.Resources.Remove(info.StoryboardName);
            if (info.CompletedHandler != null)
                info.CompletedHandler.BeginInvoke(info.Args, null, null);
            // Invoke LayoutChanged event
            ObjectLayoutChangedEventArgs args = null;
            switch(info.Args.DstProperty)
            {
                case ObjectAnimationDstType.PositionX:
                case ObjectAnimationDstType.PositionY:
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Position, Position, Position);
                    break;
                case ObjectAnimationDstType.ScaleX:
                case ObjectAnimationDstType.ScaleY:
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Scale, new Size(ScaleX, ScaleY), new Size(ScaleX, ScaleY));
                    break;
                case ObjectAnimationDstType.Orientation:
                    args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Orientation, Orientation, Orientation);
                    break;
            }

            if (args != null && LayoutChanged != null)
                LayoutChanged(this, args);
        }

        public void Close()
        {
            UTableHelper.PutCommand(new ObjectCommand(this.Owner, ObjectCommandType.Close, this));
            if (Closed != null)
                Closed(null, null);
        }

        public event EventHandler Closed;

        public event EventHandler OwnerChanged;

        public event ObjectLayoutChangedEventHandler LayoutChanged;

        public event EventHandler ObjectLoaded;

        public event EventHandler LayoutStateChanged;

        public event EventHandler OperatingStateChanged;

        public event ObjectChildrenChangedEventHandler ChildrenChanged;

		public event ObjectDragEventHandler DragStarted;

		public event ObjectDragEventHandler DragEnded;

        #endregion

        #region IObjectEntity Members

        public IVisualTree ControlTree
        {
            get { return _controlTree; }
        }

        public ObservableCollection<IObjectEntity> Children
        {
            get { return _children; }
        }

        public ILocalObjectManager LocalObjectManager
        {
            get
            {
                return _localObjectManager;
            }
            set
            {
                _localObjectManager = value;
            }
        }

        public Boolean HitContent(HitTestParameters hitTestParameters)
        {
            return true;
        }

        public HitTestResult HitTest(HitTestParameters hitTestParameters)
        {
            // only handle point hit now
            if (!(hitTestParameters is PointHitTestParameters))
                return null;
            Point point = (hitTestParameters as PointHitTestParameters).HitPoint;

            // hit test prerequisite
            if ((this.Visibility != Visibility.Visible) ||
                (this.Opacity == 0) ||
                (this.Clip != null && !this.Clip.FillContains(point)))
            {
                return new PointHitTestResult(null);
            }

            // hit children firstly
            _sortedChildren.Sort(_objectComparer);
            foreach (IObjectEntity child in _sortedChildren)
            {
                HitTestResult result = child.HitTest(new PointHitTestParameters(this.TranslatePoint(point, child)));
                if (result != null && result.VisualHit != null)
                    return result;
            }

            // finally hit the object itself
            if ((this.IsHitTestVisible == true) &&
                (this.IsHitTestEnabled == true) &&
                ((this.Clip != null && this.Clip.FillContains(point)) ||
                (this.Clip == null && (point.X >= 0 && point.X <= this.Width && point.Y >= 0 && point.Y <= this.Height))))
            {
                return new PointHitTestResult(this);
            }
            return new PointHitTestResult(null);
        }

        public Point TranslatePoint(Point point, IVisualNode relativeTo)
        {
            if (relativeTo is UObjectEntity)
            {
                return base.TranslatePoint(point, relativeTo as UObjectEntity);
            }

            return new Point(double.NaN, double.NaN);
        }

        public IObjectEntity GetObjectByID(String id)
        {
            if (this.ID == id)
                return this;
            if (_IDMap.ContainsKey(id))
                return _IDMap[id];
            foreach (IObjectEntity child in this.Children)
            {
                IObjectEntity result = child.GetObjectByID(id);
                if (result != null)
                    return result;
            }
            return null;
        }

        public virtual void ExecuateCommand(ContentCommand cmd)
        {
            SendContentCommand(cmd);
        }

		public void RaiseEvent(ObjectEventType type, object args)
		{
			switch (type)
			{
				case ObjectEventType.LayoutChanged:
					if (LayoutChanged != null)
					{
						LayoutChanged(this, args as ObjectLayoutChangedEventArgs);
					}
					break;
				case ObjectEventType.ObjectLoaded:
					if (ObjectLoaded != null)
					{
						ObjectLoaded(this, args as EventArgs);
					}
					break;
				case ObjectEventType.Closed:
					if (Closed != null)
					{
						Closed(this, args as EventArgs);
					}
					break;
				case ObjectEventType.OperatingStateChanged:
					if (OperatingStateChanged != null)
					{
						OperatingStateChanged(this, args as EventArgs);
					}
					break;
				case ObjectEventType.LayoutStateChanged:
					if (LayoutStateChanged != null)
					{
						LayoutStateChanged(this, args as EventArgs);
					}
					break;
				case ObjectEventType.OwnerChanged:
					if (OwnerChanged != null)
					{
						OwnerChanged(this, args as EventArgs);
					}
					break;
				case ObjectEventType.ChildrenChanged:
					if (ChildrenChanged != null)
					{
						ChildrenChanged(this, args as ObjectChildrenChangedEventArgs);
					}
					break;
				case ObjectEventType.DragStarted:
					if (DragStarted != null)
					{
						DragStarted(this, args as ObjectDragEventArgs);
					}
					break;
				case ObjectEventType.DragEnded:
					if (DragEnded != null)
					{
						DragEnded(this, args as ObjectDragEventArgs);
					}
					break;
			}
		}

        #endregion

        #region  IVisualNode Members

        public String ID
        {
            get { return _id; }
        }


        public new double Width
        {
            get
            {
                return base.Width;
            }
            set
            {
                if (Width != value)
                {
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Size,
                        new Size(Width, Height), new Size(value, Height));
                    base.Width = value;
                    if (_settgingProperty != PropertyType.Width)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.Width, value));
                    if (LayoutChanged != null)
                        LayoutChanged(this, args);
                }
            }
        }

        public new double Height
        {
            get
            {
                return base.Height;
            }
            set
            {
                if (Height != value)
                {
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Size,
                        new Size(Width, Height), new Size(Width, value));
                    base.Height = value;
                    if (_settgingProperty != PropertyType.Height)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.Height, value));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        public new Geometry Clip
        {
            get
            {
                return _clip;
            }
            set
            {
                if (this._clip != value)
                {
                    Geometry clip = null;
                    if (value != null)
                    {
                        clip = value.CloneCurrentValue();
                        clip.Freeze();
                    }
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Clip, Clip, clip);
                    _clip = value;
                    if (_settgingProperty != PropertyType.Clip)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.Clip, clip));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        public new Visibility Visibility
        {
            get 
            {
                return base.Visibility;
            }
            set
            {
                if (Visibility != value)
                {
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Visibility,
                        Visibility, value);
                    base.Visibility = value;
                    if (_settgingProperty != PropertyType.Visibility)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.Visibility, value));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        public new bool IsHitTestVisible
        {
            get 
            {
                return base.IsHitTestVisible;
            }
            set
            {
                if (IsHitTestVisible != value)
                {
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.IsHitTestVisible,
                        IsHitTestVisible, value);
                    base.IsHitTestVisible = value;
                    if (_settgingProperty != PropertyType.IsHitTestVisible)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.IsHitTestVisible, value));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        internal bool IsHitTestEnabled
        {
            get
            {
                return _isHitTestEnabled;
            }
            set
            {
                if (IsHitTestEnabled != value)
                {
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.IsHitTestEnabled,
                        IsHitTestEnabled, value);
                    _isHitTestEnabled = value;
                    if (_settgingProperty != PropertyType.IsHitTestEnabled)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.IsHitTestEnabled, value));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        public new double Opacity
        {
            get
            {
                return base.Opacity;
            }
            set
            {
                if (Opacity != value)
                {
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Opacity,
                        Opacity, value);
                    base.Opacity = value;
                    if (_settgingProperty != PropertyType.Opacity)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.Opacity, value));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        public new Brush Background
        {
            get { return base.Background; }
            set
            {
                if (Background != value)
                {
                    Brush background = null;
                    if (value != null)
                    {
                        background = value.CloneCurrentValue();
                        background.Freeze();
                    }
                    ObjectLayoutChangedEventArgs args = new ObjectLayoutChangedEventArgs(ObjectLayoutType.Background, Background, background);
                    // base.Background = value;
                    if (_settgingProperty != PropertyType.Background)
                        ChangeContentLayoutProperty(new UObject.LayoutPropertyChangedEventArgs(
                            UObject.LayoutPropertyType.Background, background));
                    if (LayoutChanged != null && NotifyLayoutChangeEnabled)
                        LayoutChanged(this, args);
                }
            }
        }

        #endregion

        #region IInputNode Members

        public void RaiseEvent(InputEventArgs e)
        {
            if (InputReceived != null)
            {
                InputReceived(e);
            }
            OnInputReceived(e);
        }

        public virtual void OnInputReceived(InputEventArgs args)
        {
            // Object Input Preview
            if (args is MultiTouchEventArgs)
            {
                _multiTouchProcessor.RaiseMultiTouchEvent(args as MultiTouchEventArgs);
            }
        }

        public bool CaptureFinger(int fingerId)
        {
            bool captured = UTableHelper.CaptureFinger(this, fingerId, true);
            if (captured && GotFingerCapture != null)
            {
                GotFingerCapture(this, new FingerEventArgs());
            }
            return captured;
        }

        public bool UncaptureFinger(int fingerId)
        {
            bool uncaptured = UTableHelper.UncaptureFinger(this, fingerId);

            if (uncaptured && LostFingerCapture != null)
            {
                LostFingerCapture(this, new FingerEventArgs());
            }

            return uncaptured;
        }

        public event FingerEventHandler GotFingerCapture;

        public event FingerEventHandler LostFingerCapture;

        public event InputEventHandler InputReceived;

        #endregion

    }
}
